/*
 * File: Genetic.c
 *
 * Integrantes:
 * 05-37840 Jormar Arellano
 * 05-38163 Sabrina Fernández
 */

#include "graph.h"
#include "arraylist.h"
#include "util.h"
#include "LocalSearch.h"
#include "population.h"
#include "Genetic.h"

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

void genetic_defaults() {
	parent_num = 0;
	couple_num = 0;
	mutant_num = 0;
	genetic_Psize = 30;
	genetic_Giter = 5;
	genetic_MOperc = 0.30;
	genetic_COperc = 0.90;
	firstPointCut = genetic_Psize/3;
	secondPointCut = firstPointCut*2;
	mutationPoint = 10;
}

void getPopulationSize() {
	parent_num = (int)(genetic_MOperc * genetic_Psize);
	couple_num = (int)(genetic_COperc*(genetic_Psize - parent_num));
	if(couple_num%2 == 1) {
		couple_num++;
	}
	mutant_num = genetic_Psize - parent_num - couple_num;
}

Population *initial_population(Graph *graph) {
	int i,j;
	double myFitness;
	ArrayList *list;
	Chromosome *chrom, *chaux = newChromosome();
	Population *pop = newPopulation(genetic_Psize);

	// Crear Pize cromosomas.
	for (i=0; i<genetic_Psize; i++) {
		
		// Generar arreglo aleatorio y hallamos su fitness.
		list = newChromosomeArray(graph-> vertex_number);
		for (j=0; j<list -> length; j++) {
			setColor(graph, graph-> vertex-> data[j], *(int *)(list-> data[j]));
		}
		myFitness = (double)(penaltyCost(graph));
		chrom = newChromosome(),
		
		// Agregar a la lista de cromosomas el arreglo y su fitness.
		chrom-> used = 0;
		chrom-> rank = myFitness;
		chrom-> colors = list;
		arraylist_append(pop-> chrom, chrom);
	}
	// Encontramos el mejor y lo actualizamos.
	chaux = findBetter(pop);
	pop-> better = chaux;
	pop = makeFeasible(pop,graph);

	return pop;
}

Graph *genetic(Graph *graph) {
	int i, j = 0, k=0, random = 0;
	Chromosome *chrom, *chrom1;
	ArrayList *cross_list, *mutant_list, *co_aux;

	// Se obtienen los tamaños de las poblaciones.
	getPopulationSize();

	// Se crea una poblacion inicial.
	Population *pop = newPopulation(genetic_Psize), *aux;

	// Buscamos una poblacion inicial de cromosomas.
	pop = initial_population(graph);

	for(i=0; i<genetic_Giter; i++) {
		aux = newPopulation(genetic_Psize);
		cross_list = arraylist_new(0);
		mutant_list = arraylist_new(0);
		co_aux = arraylist_new(0);

		// Una poblacion que continua.
		while (j<parent_num) {
			random = rand()%genetic_Psize;
			chrom = newChromosome();
			chrom = (Chromosome *)(pop-> chrom-> data[random]);
			if (chrom-> used == 0) {
				arraylist_append(aux-> chrom, chrom);
				chrom-> used = 1;
				pop-> chrom-> data[random] = chrom;
				j++;
			}
		}
		
		for (j=0; j<pop-> chrom-> length; j++) {
		// Se realiza una mutacion.
			if(k != mutant_num) {
				chrom = newChromosome();
				chrom = (Chromosome *)(pop-> chrom-> data[j]);
				if (chrom-> used == 0) {
					arraylist_append(mutant_list, chrom);
					chrom-> used = 1;
					pop-> chrom-> data[j] = chrom;
					k++;
				}
			} else {
				chrom = newChromosome();
				chrom = (Chromosome *)(pop-> chrom-> data[j]);
				if (chrom-> used == 0) {
					arraylist_append(cross_list, chrom);
					chrom-> used = 1;
					pop-> chrom-> data[j] = chrom;
				}
			}
		}
		cross_list = crossoverTwoPoint(cross_list, firstPointCut, secondPointCut);
		mutant_list = mutationKSwap(mutant_list, mutationPoint);

		// Agregamos los que mutaron y los hijos.
		for (j=0; j<cross_list-> length; j++) {
			chrom = newChromosome();
			chrom = (Chromosome *)(cross_list-> data[j]);
			arraylist_append(aux-> chrom, chrom);
		}
		for (j=0; j<mutant_list-> length; j++){
			chrom = newChromosome();
			chrom = (Chromosome *)(mutant_list-> data[j]);
			arraylist_append(aux-> chrom, chrom);
		}

		if (aux-> chrom-> length != pop-> chrom-> length) {
			printf("La poblacion no tiene el tamaño esperado\n");
		}
		
		aux = makeFeasible(aux, graph);
		aux = fixPopulation(aux);
	
		// Tomamos la nueva poblacion como nueva.
		chrom = newChromosome();
		chrom1 = newChromosome();
		chrom = pop-> better;
		chrom1 = aux-> better;
		if (chrom->rank > chrom1->rank){
			pop = newPopulation(pop-> chrom-> length);
			pop = clonePopulation(aux);
		} else {
			pop = newPopulation(pop-> chrom-> length);
			pop = clonePopulation(aux);
			pop->better = chrom1;
		}
		
		
		// Actualizamos el grafo
		chrom = newChromosome();
		chrom = (Chromosome *)(pop-> better);
		for (k=0; k<chrom-> colors-> length; k++){
			setColor(graph,graph-> vertex-> data[k],*(int *)(chrom-> colors-> data[k]));
		}
	}
	return graph;
}
